使用 .NET 產生帶有浮水印的 Excel
TLDR
- Excel 無內建浮水印功能,可透過設定「滿版背景圖片」或「頁首圖片」來模擬。
- 產生浮水印圖片時,需根據 Excel 的
PaperSize設定調整圖片尺寸,並注意橫向列印時需對調寬高。 - EPPlus 可透過
sheet.BackgroundImage.Image設定背景,但需注意 .NET 6+ 對System.Drawing.Common的支援限制。 - NPOI 無直接 API,需透過底層
POIXMLDocumentPart自定義VmlDrawing並手動操作 XML 結構來掛載圖片。 - 建議使用
SkiaSharp取代System.Drawing.Common以解決跨平台相容性問題。
產生滿版浮水印圖片的原理
Excel 的浮水印效果取決於檢視模式:
- 標準模式與整頁模式:可透過設定 Background Image 顯示。
- 整頁模式與列印:可透過設定 Header Image 顯示。
在產生圖片前,必須先取得目標紙張大小(PaperSize)。以下為常見紙張的像素參考(以預設解析度為準):
| PaperName | Width | Height |
|---|---|---|
| A4 | 827 | 1169 |
| A3 | 1169 | 1654 |
| Letter | 850 | 1100 |
調整圖片尺寸
若圖片尺寸不符合紙張,需進行縮放並置中處理。若 PaperSize 為橫向,請務必將 width 與 height 參數對調。
csharp
public Image ResizeImageBackgroundToFullPage(Image watermark, int width, int height) {
if (watermark.Width > width || watermark.Height > height) {
using (Image image = ZoomOutImage(width, height)) {
return ResizeImageBackgroundToFullPageInternal(width, height, image);
}
}
return ResizeImageBackgroundToFullPageInternal(width, height, watermark);
}使用 EPPlus 產生浮水印
什麼情況下會遇到這個問題:當開發環境使用 EPPlus 且專案目標為 .NET Framework 或舊版 .NET 時。
在 EPPlus 中,可以直接透過 HeaderFooter 或 BackgroundImage 屬性設定:
csharp
sheet.HeaderFooter.OddHeader.InsertPicture(watermark, PictureAlignment.Centered);
sheet.BackgroundImage.Image = watermark;WARNING
EPPlus 6 以後的版本移除了對 System.Drawing.Common 的依賴,若升級至新版,需改用其他繪圖函式庫處理圖片物件。
使用 NPOI 產生浮水印 (XLSX)
什麼情況下會遇到這個問題:當專案無法使用 EPPlus,或需要更底層的控制權時。
NPOI 沒有直接設定背景圖的 API,必須手動建立 VmlDrawing 並處理 XML 關聯。
1. 定義 VmlDrawing 類別
需繼承 POIXMLDocumentPart 並實作 Commit 方法,將圖片資訊寫入 VML 結構中:
csharp
private class VmlDrawing : POIXMLDocumentPart {
public string PictureRelId { get; set; }
public Image Image { get; set; }
protected override void Commit() {
PackagePart part = GetPackagePart();
using Stream @out = part.GetOutputStream();
// 將 Pixel 轉換為 Points 單位
float width = Image.Width * 72 / Image.HorizontalResolution;
float height = Image.Height * 72 / Image.VerticalResolution;
// 寫入 VML XML 結構
// 注意:此處需根據實際需求填入正確的 XML 節點與屬性
}
}2. 設定浮水印關聯
透過 AddRelation 將圖片與 Sheet 進行關聯,並將 legacyDrawingHF 指向自定義的 VmlDrawing:
csharp
// 將圖片加入 Workbook
int pictureIdx = workbook.AddPicture(imageMs.ToArray(), PictureType.PNG);
POIXMLDocumentPart docPart = workbook.GetAllPictures()[pictureIdx] as POIXMLDocumentPart;
// 建立 VML 關聯
VmlDrawing drawing = (VmlDrawing)sheet.CreateRelationship(VmlRelation.Instance, XSSFFactory.GetInstance(), drawingNumber);
drawing.Image = watermark;
drawing.PictureRelId = headerRelPart.Relationship.Id;
// 設定頁首圖片
sheet.Header.Center = HeaderFooter.PICTURE_FIELD.sequence;
sheet.GetCTWorksheet().legacyDrawingHF = new CT_LegacyDrawing {
id = sheet.GetRelationId(drawing)
};範例專案
更多實作細節與 .NET 10 環境下的調整(改用 SkiaSharp),請參考:CloudyWing/ExcelWatermarkSample。
異動歷程
- 初版文件建立。
- 補上 GitHub 範例專案連結。